use crate::config::device_config::DeviceConfig;
use crate::config::update_config::UpdateConfig;
use crate::core::entity::device_candidate::DeviceCandidate;
use crate::core::entity::whitelist::Whitelist;
use crate::core::model::channel_setting::ChannelSetting;
use crate::core::model::native_version_setting::NativeVersionSetting;
use share::core::channel::Channel;
use share::core::device_id::DeviceId;
use share::core::native_version::NativeVersion;
use std::collections::HashMap;
use std::convert::Infallible;
use std::net::IpAddr;
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use tracing::warn;
use uuid::Uuid;
use warp::Filter;
use warp::cors::Builder;
use warp::http::Method;
use warp::hyper::body::HttpBody;

pub type ChannelSettings = HashMap<Channel, ChannelSetting>;
pub type HotUpdateSettings =
    HashMap<Channel, Arc<RwLock<HashMap<NativeVersion, NativeVersionSetting>>>>;

pub type WhitelistSettings = HashMap<Channel, Arc<RwLock<HashMap<NativeVersion, Whitelist>>>>;

pub struct CustomFilter {
    inner_ip: Arc<RwLock<Option<IpAddr>>>,
    channel_settings: Arc<RwLock<ChannelSettings>>,
    hot_update_settings: Arc<RwLock<HotUpdateSettings>>,
    whitelist_settings: Arc<RwLock<WhitelistSettings>>,
    devices: Arc<RwLock<HashMap<DeviceId, Option<String>>>>,
    device_candidates: Arc<Mutex<Vec<DeviceCandidate>>>,
}

impl CustomFilter {
    pub fn new(update_config: UpdateConfig, devices: Vec<DeviceConfig>) -> Self {
        let mut channel_settings = ChannelSettings::new();
        let mut hot_update_settings = HotUpdateSettings::new();
        let mut whitelist_settings = WhitelistSettings::new();

        for channel_config in update_config.channels.into_iter() {
            let name = channel_config.channel;
            let native_version = channel_config.native_version;
            let jump_url = channel_config.jump_url;

            let old = channel_settings.insert(
                name,
                ChannelSetting {
                    name,
                    native_version,
                    jump_url,
                },
            );

            if let Some(old) = old {
                warn!(?old, "Old channel setting is replaced.")
            }

            let whitelist_setting = channel_config
                .hot_update_settings
                .iter()
                .map(|(nv, _)| (nv.clone(), Whitelist::new()))
                .collect();

            let old = whitelist_settings.insert(name, Arc::new(RwLock::new(whitelist_setting)));
            if let Some(old) = old {
                warn!(?old, "Old whitelist is replaced.")
            }

            let update_setting = channel_config
                .hot_update_settings
                .into_iter()
                .map(|(nv, config)| (nv, config.into()))
                .collect();

            let old = hot_update_settings.insert(name, Arc::new(RwLock::new(update_setting)));
            if let Some(old) = old {
                warn!(?old, "Old hot update setting is replaced.")
            }
        }

        let devices = devices.into_iter().map(DeviceConfig::take_all).collect();

        Self {
            inner_ip: Arc::new(RwLock::new(None)),
            channel_settings: Arc::new(RwLock::new(channel_settings)),
            hot_update_settings: Arc::new(RwLock::new(hot_update_settings)),
            whitelist_settings: Arc::new(RwLock::new(whitelist_settings)),
            devices: Arc::new(RwLock::new(devices)),
            device_candidates: Arc::new(Mutex::new(Vec::new())),
        }
    }

    pub fn inner_ip(
        &self,
    ) -> impl Filter<Extract = (Arc<RwLock<Option<IpAddr>>>,), Error = Infallible> + Clone + use<>
    {
        let inner_ip = self.inner_ip.clone();
        warp::any().map(move || inner_ip.clone())
    }

    pub fn channel_settings(
        &self,
    ) -> impl Filter<Extract = (Arc<RwLock<ChannelSettings>>,), Error = Infallible> + Clone + use<>
    {
        let channel_settings = self.channel_settings.clone();
        warp::any().map(move || channel_settings.clone())
    }

    pub fn hot_update_settings(
        &self,
    ) -> impl Filter<Extract = (Arc<RwLock<HotUpdateSettings>>,), Error = Infallible> + Clone + use<>
    {
        let hot_update_settings = self.hot_update_settings.clone();
        warp::any().map(move || hot_update_settings.clone())
    }

    pub fn whitelist_settings(
        &self,
    ) -> impl Filter<Extract = (Arc<RwLock<WhitelistSettings>>,), Error = Infallible> + Clone + use<>
    {
        let whitelist_settings = self.whitelist_settings.clone();
        warp::any().map(move || whitelist_settings.clone())
    }

    pub fn devices(
        &self,
    ) -> impl Filter<Extract = (Arc<RwLock<HashMap<DeviceId, Option<String>>>>,), Error = Infallible>
    + Clone
    + use<> {
        let devices = self.devices.clone();
        warp::any().map(move || devices.clone())
    }

    pub fn device_candidates(
        &self,
    ) -> impl Filter<Extract = (Arc<Mutex<Vec<DeviceCandidate>>>,), Error = Infallible> + Clone + use<>
    {
        let candidates = self.device_candidates.clone();
        warp::any().map(move || candidates.clone())
    }

    pub fn cors(&self) -> Builder {
        warp::cors()
            .allow_any_origin()
            .allow_headers(vec![
                "Access-Control-Allow-Origin",
                "Origin",
                "Accept",
                "X-Requested-With",
                "Content-Type",
            ])
            .allow_methods(vec![
                Method::GET,
                Method::POST,
                Method::PUT,
                Method::DELETE,
                Method::OPTIONS,
                Method::PATCH,
                Method::TRACE,
            ])
    }
}
